/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package grafrajz;

import grafrajz.utils.*;
import java.awt.Point;
import java.io.*;
import java.util.ArrayList;

/**
 *
 * @author adam_fejes_dell
 * 
 * A gráf logikai egysége. Ebben az osztályban tárolja a program a gráfot és itt vannak megvalósítva a gráf műveletek legfelső rétege
 */
public class GraphLogic {
    private Graph graph;
    final private int CANVAS_WIDTH, CANVAS_HEIGHT, NODE_SIZE;
    
    private ArrayList<DrawableNode> drawableNodes;
    private ArrayList<DrawableEdge> drawableEdges;
    
    private GrafRajzWindow window;

    public GraphLogic(int WIDTH, int HEIGHT, int NODE_SIZE, GrafRajzWindow frame) {
        this.CANVAS_WIDTH = WIDTH;
        this.CANVAS_HEIGHT = HEIGHT;
        this.NODE_SIZE = NODE_SIZE;
        
        this.window = frame;
        
        drawableNodes = new ArrayList<DrawableNode>();
        drawableEdges = new ArrayList<DrawableEdge>();
    }

    /**
     * Új gráf létrehozása
     * @param isWei gráf súlyozottsága
     * @param isDir gráf irányítottsága
     * @param isTree gráf fa-e
     */
    public void newGraph(boolean isWei, boolean isDir, boolean isTree){
        if(isTree){
            graph = new Tree(isWei, isDir);
        }
        else{
            graph = new Graph(isWei, isDir);
        }
        
        updateDrawableNodes();
        updateDrawableEdges();
    }
    
    /**
     * Új csúcsot ad hozzá a gráfhoz
     * @param nodeName Új csúcs neve
     * @return 
     */
    public boolean addNode(String nodeName){
        Node newNode = new Node(nodeName);
        if(graph.addNode(newNode)){
            //addDrawableNode(newNode);
            updateDrawableNodes();
            return true;
        }
        else{
            return false;
        }
    }   
    
    /**
     * Csúcs eltávolítása
     * @param nodeName az eltávolítandó csúcs neve
     * @return 
     */
    public boolean removeNode(String nodeName){
        Node nodeToRemove = new Node(nodeName); 
        boolean nodeHasEdges = graph.hasEdges(nodeToRemove);
        if(graph.removeNode(nodeToRemove)){
            updateDrawableNodes();
            if(nodeHasEdges){
                for(int i = 0; i < drawableEdges.size(); i++){
                    ////System.out.println("Checking: " + drawableEdges.get(i).getEdge().getSource() + " - " + drawableEdges.get(i).getEdge().getDest());
                    if(drawableEdges.get(i).getEdge().getSource().getName().equals(nodeName) || drawableEdges.get(i).getEdge().getDest().getName().equals(nodeName)){
                        drawableEdges.remove(i);
                        i--; //Ha törlés után továbblép akkor a következő kimaradna!
                    } 
                }
            }
            //System.out.println("Node count: " + graph.getNodeCount());
            return true;
        }
        else
            return false;
    }
    
    /**
     * Új él hozzáadása (súlyozott)
     * @param sourceNodeName él forráscsúcsának neve
     * @param edgeWeight él súlya
     * @param destNodeName él célcsúcsának neve
     * @return 
     */
    public boolean addEdge(String sourceNodeName, int edgeWeight, String destNodeName){
        Edge newEdge = new Edge(new Node(sourceNodeName), new Node(destNodeName), edgeWeight); 
        if(graph.addEdge(newEdge)){
            addDrawableEdge(newEdge);
            return true;
        }
        else
            return false;
    }
    
    /**
     * Új él hozzáadása (súlyozatlan)
     * @param sourceNodeName él forráscsúcsának neve
     * @param destNodeName él célcsúcsának neve
     * @return 
     */
    public boolean addEdge(String sourceNodeName, String destNodeName){
        Edge newEdge = new Edge(new Node(sourceNodeName), new Node(destNodeName)); 
        if(graph.addEdge(newEdge)){
            addDrawableEdge(newEdge);
            //updateDrawableEdges();
            return true;
        }
        else
            return false;
    }
    
    /**
     * Él eltávolítása
     * @param e eltávolítandó él
     * @return 
     */
    public boolean removeEdge(Edge e){
        //Edge edgeToRemove = new Edge(new Node(sourceNodeName), new Node(destNodeName), edgeWeight);
        if(graph.removeEdge(e)){
            removeDrawableEdge(e);
            //updateDrawableEdges();
            return true;
        }
        else
            return false;
    }
    
    /**
     * Kirajzolható csúcsok karbantartása
     */
    private void updateDrawableNodes(){
        ArrayList<Node> nodes = getAllNodes();
        drawableNodes.removeAll(drawableNodes);
        for(int i = 0; i < nodes.size(); i++){
            drawableNodes.add(new DrawableNode(nodes.get(i), calculateNodePos(nodes.get(i), i)));
        }
        
        updateDrawableEdges();
        
    }
    
    /**
     * Kirajzolható élek karbantartása
     */
    private void updateDrawableEdges(){
        ArrayList<Edge> edges = getAllEdges();
        drawableEdges.removeAll(drawableEdges);
        for(Edge e : edges){
            Point[] p = calculateEdgePos(e);
            drawableEdges.add(new DrawableEdge(e, p[0], p[1]));
        }
    }
    
    /**
     * Kirajzolható csúcs hozzáadása
     */
    private boolean addDrawableNode(Node n){
        int i = 0;
        boolean contains = false;
        while(i < drawableNodes.size() && !contains){
            contains = drawableNodes.get(i).getNode() == n;
            i++;
        }
        
        if(!contains){
            drawableNodes.add(new DrawableNode(n, calculateNodePos(n , getNodeCount())));
            return true;
        }
        else{
            return false;
        }
    }
    
    
    /**
     * Kirajzolható csúcs eltávolítása
     */
    private boolean removeDrawableNode(Node n){
        //System.out.println("Remove drawable node: " + n);
        int i = 0;
        boolean contains = false;
        while(i < drawableNodes.size() && !contains){
            contains = drawableNodes.get(i).getNode().getName() == n.getName();
            i++;
        }
        
        if(contains){
            drawableNodes.remove(i-1);
            //System.out.println("DrawAbleNodes size: " + drawableNodes.size());
            return true;
        }
        else{
            return false;
        }
    }
    
    /**
     * Kirajzolható él hozzáadása
     */
    private boolean addDrawableEdge(Edge edge){
        int i = 0;
        boolean contains = false;
        while(i < drawableEdges.size() && !contains){
            contains = drawableEdges.get(i).getEdge() == edge;
            i++;
        }
        
        if(!contains){
            Point[] points = calculateEdgePos(edge);
            drawableEdges.add(new DrawableEdge(edge, points[0], points[1]));
            return true;
        }
        else{
            return false;
        }
    }
    
    
    /**
     * Kirajzolható él eltávolítása
     */
    private boolean removeDrawableEdge(Edge e){
        int i = 0;
        boolean contains = false;
        while(i < drawableEdges.size() && !contains){
            contains = drawableEdges.get(i).getEdge() == e;
            i++;
        }
        
        if(contains){
            drawableEdges.remove(i-1);
            return true;
        }
        else{
            return false;
        }
    }
    
    /**
     * Csúcs kirajzolási pozíciójának kiszámítása
     * @param node kirajzolandó csúcs
     * @param n csúcs sorszáma
     * @return 
     */
    private Point calculateNodePos(Node node, int n){
        //úgy kéne átszabni, hogy ahány csúcs van / 2.5 (vagy több, ki kell kísérletezni) felső egészrész db sorba rendezze őket
        int x, y;
        n++;
        
        //Ha lesz idő itt még olyan finomítások lesznek, hogy figyeljük az éleket és odébb toljuk a 
        //csúcsot ha útban van, de persze úgy hogy ne lógjon rá egy másik csúcsra, stb...
        x = (int) Math.round(CANVAS_WIDTH / 6.5 * ((4+n) %  5 + 1)) + node.hashCode() * 10 % 20;
        y = (int) Math.round(CANVAS_HEIGHT / 5 * Math.floor((4+n) / 5)) + node.hashCode() * 10 % 40;
        
        
        return new Point(x,y);
    }
    
    /**
     * Kirajzolandó él pozíciójának kiszámítása
     * @param edge kirajzolandó él
     * @return 
     */
    private Point[] calculateEdgePos(Edge edge){
       Point[] points = new Point[2];       
       int x, y;
       
       DrawableNode n = new DrawableNode(edge.getSource(), null);
       
       x = drawableNodes.get(drawableNodes.indexOf(n)).getPos().x + NODE_SIZE / 2;
       y = drawableNodes.get(drawableNodes.indexOf(n)).getPos().y + NODE_SIZE / 2;
       points[0] = new Point(x, y);
       
       n = new DrawableNode(edge.getDest(), null);
       x = drawableNodes.get(drawableNodes.indexOf(n)).getPos().x + NODE_SIZE / 2;
       y = drawableNodes.get(drawableNodes.indexOf(n)).getPos().y + NODE_SIZE / 2;
       points[1] = new Point(x, y);
       return points;
    }

    
    public ArrayList<DrawableEdge> getDrawableEdges() {
        return drawableEdges;
    }

    public ArrayList<DrawableNode> getDrawableNodes() {
        return drawableNodes;
    }
    
    public boolean isDirected(){
        return graph.isDirected();
    }
    
    public boolean isWeightened(){
        return graph.isWeightened();
    }
    
    public ArrayList getAllNodes(){
        return graph.getAllNodes();
    }
    
    public ArrayList getAllEdges(){
        return graph.getAllEdges();
    }
    
    public boolean isTree(){
        Tree tree  = new Tree(true, true);
        return graph.getClass() == tree.getClass();
    }
    
    public int getNodeCount(){
        return graph.getNodeCount();
    }
    
    public int getEdgeCount(){
        return graph.getEdgeCount();
    }
    
    /**
     * Gráf elmentése fájlba
     * @param filename cél fájl neve
     * @throws IOException 
     * A fájlokat normál szövegfájlban tárolja a program, így azok könnyen szerkeszthetők egy szövegszerkesztővel
     */
    public void saveGraph(String filename) throws IOException {

            FileOutputStream outFileStream = new FileOutputStream(filename);
            PrintStream out = new PrintStream(outFileStream);
            
            String isW, isD, isT;
            
            if(isWeightened())
                isW = "1";
            else
                isW = "0";
            
            if(isDirected())
                isD = "1";
            else
                isD = "0";
            
            if(isTree())
                isT = "1";
            else
                isT = "0";
            
            out.println(isW + isD + isT);
            
            out.println(getNodeCount());
            
            ArrayList<Node> nodes = getAllNodes();
            ArrayList<Edge> edges = getAllEdges();
            
            for(Node n : nodes){
                out.println(n.toString());
            }
            
            out.println(getEdgeCount());
            
            for(Edge e : edges){
                String weight = "";
                if(isWeightened())
                    weight = "\t" + e.getWeight();
                out.println(e.getSource().toString() + "\t" + e.getDest().toString() + weight);
            }   
            
            outFileStream.close();
    }

    /**
     * Gráf beolvasása fájlból
     * @param filename beolvasandó fájl neve
     * @throws IOException
     * @throws ClassNotFoundException 
     */
    public void loadGraph(String filename) throws IOException, ClassNotFoundException {
        FileInputStream inFileStream = null;
        DataInputStream in = null;
        
        //System.out.println("Elkezdem betölteni");
        
        try {
            inFileStream = new FileInputStream(filename);
            in = new DataInputStream(inFileStream);

        } catch (IOException ioEx) {}

        try {
            while (in.available() != 0) {
                String line;
                boolean isW, isD, isT;

                line = in.readLine();
                if(line.charAt(0) == '0')
                    isW = false;
                else
                    isW = true;
                
                if(line.charAt(1) == '0')
                    isD = false;
                else
                    isD = true;
                
                if(line.charAt(2) == '0')
                    isT = false;
                else
                    isT = true;
                
                newGraph(isW, isD, isT);
                //System.out.println("Gráf létrehozva");
                
                int n = Integer.valueOf(in.readLine());
                
                for(int i = 0; i < n; i++){
                    line = in.readLine();
                    addNode(line);
                    //System.out.println("Csúcs betöltve: " + line);
                }
               
                n = Integer.valueOf(in.readLine());
                for(int i = 0; i < n; i++){
                    line = in.readLine();
                    //System.out.println(line);
                    String temp[] = line.split("\t");
                    Edge e;
                    if(isDirected())
                        e = new Edge(new Node(temp[0]), new Node(temp[1]), Integer.valueOf(temp[2]));
                    else
                        e = new Edge(new Node(temp[0]), new Node(temp[1]));
                    
                    graph.addEdge(e);
                }

            }
            inFileStream.close();
            in.close();
            
            window.updateWindow(window);
            updateDrawableNodes();
            
        } catch (IOException ex) {

        }
        
    }
    
    
    public Algorithms getAlgo(){
        return graph.getAlgo();
    }
    
    public Graph getGraph(){
        return graph;
    }
    
}
